
/* Challenge a claimed AIK public key and EK certificate */
/* This encrypts a specified string to the Endorsement Key, which will only
 * decrypt it if the corresponding AIK is legitimate. */
/* Also checks that EK cert is valid using the required public key */

/*
 * Copyright (c) 2009 Hal Finney
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 */

#include <stdio.h>
#include <string.h>
#include <memory.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/rand.h>
#include <arpa/inet.h>
#define DEBUG 1
#define TPM_ALG_AES				0x00000006
#define TPM_ES_SYM_CBC_PKCS5PAD	((UINT16)0x00ff)
#define TPM_SS_NONE				((UINT16)0x0001)

#ifndef BYTE
#define BYTE unsigned char
#endif

#ifndef UINT16
#define UINT16 unsigned short
#endif

#ifndef UINT32
#define UINT32 unsigned
#endif


static int
verifyCert(char* keyfile, char* certfile);

int
main (int ac, char **av)
{
	FILE		*f_in;
	FILE		*f_out;
	BYTE		key[128/8];
	BYTE		iv[16];
	BYTE		asymPlain[8 + sizeof(key) + SHA_DIGEST_LENGTH];
	unsigned char oaepPad[4] = "TCPA";
	BYTE		*asymPadded;
	UINT32		asymPaddedLength;
	BYTE		*asymEnc;
	UINT32		asymEncLength;
	BYTE		*chal;
	UINT32		chalLen;
	BYTE		*symEnc;
	UINT32		symEncLength;
	BYTE		*symAttest;
	UINT32		symAttestLength;
	EVP_CIPHER_CTX ctx;
	RSA			*ekRsa;
	int			trousersIVMode = 1;
	int			out1, out2;
    BYTE        *blob;
    UINT32      blobLen;
    UINT32		tt[1];


	if (ac != 6) {
		fprintf (stderr, "Usage: %s secretfile cetfile EKkey aikblob outchallengefile \n", av[0]);
		exit (1);
	}

	/* Read challenge */
	if ((f_in = fopen (av[1], "r")) == NULL) {
		fprintf (stderr, "Unable to open file %s\n", av[1]);
		exit (1);
	}
	fseek (f_in, 0, SEEK_END);
	chalLen = ftell (f_in);
	fseek (f_in, 0, SEEK_SET);
	chal = malloc (chalLen);
	if (fread (chal, 1, chalLen, f_in) != chalLen) {
		fprintf (stderr, "Unable to read file %s\n", av[1]);
		exit (1);
	}
	fclose (f_in);
    
    /* Read EKKey */
	
    if ((f_in = fopen (av[3], "rb")) == NULL) {
		fprintf (stderr, "Unable to open file %s\n", av[3]);
        exit(1);
    }
    if ((ekRsa = PEM_read_RSA_PUBKEY(f_in, NULL, NULL, NULL)) == NULL) {
	    fclose(f_in);
		fprintf (stderr, "Unable to read Endoresment Key \n");
        exit(1);
    }
    fclose(f_in);

	/* Construct encrypted output challenge */
	RAND_bytes (key, sizeof(key));
	RAND_bytes (iv, sizeof(iv));


	/* SYM_CA_Structure Encrypt with EK */
    /*
     * Creating the AYSM_CA_CONTENT for ecrypting the session key
     * typedef struct  TPM_ASYM_CA_CONTENTS
        {
            TPM_SYMMETRIC_KEY sessionKey; // 
            TPM_DIGEST idDigest; //sha1 of aik 
        } TPM_ASYM_CA_CONTENTS;
     
      typedef struct tdTPM_SYMMETRIC_KEY 
      {
            TPM_ALGORITHM_ID algId;
            TPM_ENC_SCHEME encScheme;
            UINT16 size;
           [size_is(size)] BYTE* data;
        } TPM_SYMMETRIC_KEY;

     **/

	/* Prepare buffer to be RSA encrypted to endorsement key */
    /*Fill the details of TPM_SYMMETRIC_KEY for the session key */
	((UINT32 *)asymPlain)[0] = htonl(TPM_ALG_AES);
	((UINT16 *)asymPlain)[2] = htons(TPM_ES_SYM_CBC_PKCS5PAD);
	((UINT16 *)asymPlain)[3] = htons(sizeof(key));
	memcpy (asymPlain+8, key, sizeof(key));
    
    // this is sha1 of the AIK blob
    // can we generate blob with AIK key or vise versa   ??
    // If so then we dont need to pass blob or key ...TODO
    
    /* Read AIK blob */

    if ((f_in = fopen (av[4], "rb")) == NULL) {
		fprintf (stderr, "Unable to open file %s\n", av[4]);
		exit (1);
	}
   
    fseek (f_in, 0, SEEK_END);
	blobLen = ftell(f_in);
    fseek (f_in, 0, SEEK_SET);
	blob = malloc (blobLen);
    
    if (fread (blob, 1, blobLen, f_in) != blobLen) {
		fprintf (stderr, "Unable to read file %s\n", av[4]);
		exit (1);
	}
    //Fill in the digest portaion for the ASYM strucutre 
    // with AIK fingerprint
    // We need this fingerprint for TPM to identify that the AIK
    // blongs to the TPM ( TPM checks it during Activate_Identity call )
    SHA1 (blob, blobLen, asymPlain + 8 + sizeof(key));
   
    #ifdef DEBUG
    {   int i;
        for ( i= 0; i < 20; i++)
            printf("%02x ", asymPlain[8 + sizeof(key)+ i]);
    }   
    #endif
    fclose (f_in);
    free(blob);

	/* Must use custom padding for TPM to decrypt it */
    /*Encryt AYSM_CA_CONTET with Public EK */	
    asymPaddedLength = asymEncLength = RSA_size (ekRsa);
	asymPadded = malloc (asymPaddedLength);
	asymEnc = malloc (asymEncLength);
	RSA_padding_add_PKCS1_OAEP(asymPadded, asymPaddedLength, asymPlain,
					sizeof(asymPlain), oaepPad, sizeof(oaepPad));
	RSA_public_encrypt (asymPaddedLength, asymPadded, asymEnc, ekRsa, RSA_NO_PADDING);
	free (asymPadded);
	asymPadded = NULL;

    
	/* Encrypt challenge with key */

    /* This structure is created for TPM to retrieve encrypted credential
     * For TPM to retrieve the session key is has to have private EK
     *
     * Typedef  TPM_SYM_CA_ATTESTATION  tpm_ca_sym
     *  {
     *       UINT32 credSize             // This SHALL be the size of the credential parameter
     *       
     *       TPM_KEY_PARMS algorithm     // This SHALL be the indicator and parameters 
     *                                   //  for the symmetric algorithm (defined below)
     *
     *       BYTE* credential            // This is the result of encrypting TPM_IDENTITY_CREDENTIAL 
	 *			                         // Challange encrypted with session key is the credentials
     *  }
     *
     * Deatails of TPM_KEY Param structure
     *
     *  typedef struct tdTPM_SYMMETRIC_KEY_PARMS { 
     *    UINT32 keyLength; 
     *    UINT32 blockSize; 
     *    UINT32 ivSize; 
     *    [size_is(ivSize)] BYTE IV; 
     *  } TPM_SYMMETRIC_KEY_PARMS; 
     */   
    
    /* Encrypt the content of secret file (our challange) with session key
     * 
     * */
	symEnc = malloc (chalLen + sizeof(iv));
	EVP_CIPHER_CTX_init (&ctx);
	EVP_EncryptInit(&ctx, EVP_aes_128_cbc(), key, iv);
	EVP_EncryptUpdate (&ctx, symEnc, &out1, chal, chalLen);
	EVP_EncryptFinal_ex (&ctx, symEnc+out1, &out2);
	EVP_CIPHER_CTX_cleanup(&ctx);
	symEncLength = out1 + out2;

	/* Create TPM_SYM_CA_ATTESTATION struct to hold encrypted credential */
	symAttestLength = 28 + sizeof(iv) + symEncLength;
	symAttest = malloc (symAttestLength);
	((UINT32 *)symAttest)[0] = htonl(symEncLength);
	((UINT32 *)symAttest)[1] = htonl(TPM_ALG_AES);
	((UINT16 *)symAttest)[4] = htons(TPM_ES_SYM_CBC_PKCS5PAD);
	((UINT16 *)symAttest)[5] = htons(TPM_SS_NONE);
	((UINT32 *)symAttest)[3] = htonl(12+sizeof(iv));
	((UINT32 *)symAttest)[4] = htonl(128);		/* Key length in bits */
	((UINT32 *)symAttest)[5] = htonl(sizeof(iv));	/* Block size in bytes */
	((UINT32 *)symAttest)[6] = htonl(sizeof(iv));	/* IV size in bytes */
	memcpy (symAttest+28, iv, sizeof(iv));
	memcpy (symAttest+28+sizeof(iv), symEnc, symEncLength);
	
    if (trousersIVMode) { // We are using IV mode
		((UINT32 *)symAttest)[0] = htonl(symEncLength + sizeof(iv));
		((UINT32 *)symAttest)[3] = htonl(12);		/* Take IV to be start of symEnc */
		((UINT32 *)symAttest)[6] = htonl(0);		/* IV size in bytes */
	}
	
    free (symEnc);
	symEnc = NULL;

	if ((f_out = fopen (av[5], "wb")) == NULL) {
		fprintf (stderr, "Unable to open file %s for output\n", av[3]);
		exit (1);
	}
	/* Precede the two blocks with 4-byte lengths */
    /* Write ASYM and SYM CA struct to file 
     * These will be send to XenServer as a challange
     * We should get back our secret after XS activates the AIK
     * */
	tt[0] = htonl (asymEncLength);
	fwrite (tt, 1, sizeof(UINT32), f_out);
	fwrite (asymEnc, 1, asymEncLength, f_out);
	tt[0] = htonl (symAttestLength);
	fwrite (tt, 1, sizeof(UINT32), f_out);
	
    if (fwrite (symAttest, 1, symAttestLength, f_out) != symAttestLength) {
		fprintf (stderr, "Unable to write to file %s\n", av[3]);
		exit (1);
	}
	
    fclose (f_out);
	printf ("Challange generated for TPM !\n");
	return 0;

error:
	fprintf (stderr, "Error processing input file\n");
	return 1;
}


EVP_PKEY* read_rsa_pubkey(char* keyfile)
{

    FILE *fp = NULL;
    EVP_PKEY *pk;
    RSA *rsa;
    
    fp = fopen(keyfile,"r");
    if (!fp) {
        fprintf(stdout,"%s","Error Reading key file");
        return NULL;
    }
    rsa = RSA_new();
    if( !PEM_read_RSA_PUBKEY(fp, &rsa, NULL,NULL)) {
        ERR_print_errors_fp(stdout);
        fprintf(stdout,"%s","Error Reading public Key");
        RSA_free(rsa);
        fclose(fp);
        return NULL;
    }
    fclose(fp);
    
    if ((pk=EVP_PKEY_new()) == NULL){
        RSA_free(rsa);
        return NULL;
    }
 
	if (!EVP_PKEY_assign_RSA(pk,rsa)){
		EVP_PKEY_free(pk);
        RSA_free(rsa);
        fprintf(stdout,"%s","Error inserting key in EVP");
        return NULL;
    }
    return pk;
}

/* Check the certificate from the key */
/* this in internal function for validatin certs from
 * the public key
 * */
static int
verifyCert(char* keyfile, char* certfile)
{
	X509		*tbsX509 = NULL;
	EVP_PKEY	*pkey = NULL;
	int			rslt = -1;
    FILE        *fp = NULL;
	OpenSSL_add_all_algorithms();
	pkey = read_rsa_pubkey(keyfile);
	
    if (!pkey) {
		printf("\n Unable to read rsa public key");
		return 0;
	}
    
    fp = fopen(certfile,"rb");
    
    if(!fp) {
        fprintf(stdout,"%s","Error in reading certfile file");
        goto done;
    }

	tbsX509 = d2i_X509_fp(fp, NULL);
    
    if(!tbsX509)  
		goto done;
	
    if (X509_verify (tbsX509, pkey) != 1)
		goto done;

	fclose(fp);
    X509_free (tbsX509);
	tbsX509 = NULL;
	/* Success */
	rslt = 0;

done:
	if (pkey)
		EVP_PKEY_free (pkey);
	if (tbsX509)
		X509_free (tbsX509);
	return rslt;
}


